/*
 * Wazuh Vulnerability scanner
 * Copyright (C) 2015, Wazuh Inc.
 * May 1, 2023.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#ifndef _FACTORY_ORCHESTRATOR_HPP
#define _FACTORY_ORCHESTRATOR_HPP

#include "alertClearBuilder.hpp"
#include "arrayResultIndexer.hpp"
#include "buildAllAgentListContext.hpp"
#include "buildSingleAgentListContext.hpp"
#include "chainOfResponsability.hpp"
#include "cleanAgentInventory.hpp"
#include "cleanInventory.hpp"
#include "clearSendReport.hpp"
#include "cveSolvedAlertDetailsBuilder.hpp"
#include "cveSolvedInventorySync.hpp"
#include "eventDeleteInventory.hpp"
#include "eventDetailsBuilder.hpp"
#include "eventInsertInventory.hpp"
#include "eventPackageAlertDetailsBuilder.hpp"
#include "eventSendReport.hpp"
#include "globalSyncInventory.hpp"
#include "hotfixInsert.hpp"
#include "osScanner.hpp"
#include "packageScanner.hpp"
#include "resultIndexer.hpp"
#include "scanAgentList.hpp"
#include "scanInventorySync.hpp"
#include "scanOsAlertDetailsBuilder.hpp"
#include <memory>

/**
 * @brief FactoryOrchestrator class.
 *
 */
template<typename TPackageScanner = PackageScanner,
         typename TEventPackageAlertDetailsBuilder = EventPackageAlertDetailsBuilder,
         typename TScanOsAlertDetailsBuilder = ScanOsAlertDetailsBuilder,
         typename TCVESolvedAlertDetailsBuilder = CVESolvedAlertDetailsBuilder,
         typename TEventDetailsBuilder = EventDetailsBuilder,
         typename TAlertClearBuilder = AlertClearBuilder,
         typename TOsScanner = OsScanner,
         typename TCleanInventory = CleanInventory,
         typename TEventDeleteInventory = EventDeleteInventory,
         typename TEventInsertInventory = EventInsertInventory,
         typename TScanInventorySync = ScanInventorySync,
         typename TCVESolvedInventorySync = CVESolvedInventorySync,
         typename TCleanSendReport = ClearSendReport,
         typename TEventSendReport = EventSendReport,
         typename TResultIndexer = ResultIndexer,
         typename TDatabaseFeedManager = DatabaseFeedManager,
         typename TIndexerConnector = IndexerConnector,
         typename TScanContext = ScanContext,
         typename TBuildAllAgentListContext = BuildAllAgentListContext,
         typename TBuildSingleAgentListInfoContext = BuildSingleAgentListInfoContext,
         typename TCleanAgentInventory = CleanAgentInventory,
         typename TScanAgentList = ScanAgentList,
         typename TGlobalSyncInventory = GlobalSyncInventory,
         typename THotfixInsert = HotfixInsert,
         typename TArrayResultIndexer = ArrayResultIndexer>
class TFactoryOrchestrator final
{
private:
    TFactoryOrchestrator() = default;

public:
    /**
     * @brief Creates an orchestrator and returns it.
     *
     * @param type Scanner type.
     * @param databaseFeedManager DatabaseFeedManager object.
     * @param indexerConnector Indexer connector object.
     * @param inventoryDatabase Inventory database.
     * @param reportDispatcher Report dispatcher queue to send vulnerability reports.
     * @return std::shared_ptr<ScanContext> Abstract handler.
     */
    static std::shared_ptr<AbstractHandler<std::shared_ptr<TScanContext>>>
    create(ScannerType type,
           std::shared_ptr<TDatabaseFeedManager> databaseFeedManager,
           std::shared_ptr<TIndexerConnector> indexerConnector,
           Utils::RocksDBWrapper& inventoryDatabase,
           std::shared_ptr<ReportDispatcher> reportDispatcher)
    {
        std::shared_ptr<AbstractHandler<std::shared_ptr<TScanContext>>> orchestration;
        switch (type)
        {
            case ScannerType::PackageInsert:
                orchestration = std::make_shared<TPackageScanner>(databaseFeedManager);
                orchestration->setLast(std::make_shared<TEventInsertInventory>(inventoryDatabase));
                orchestration->setLast(std::make_shared<TEventDetailsBuilder>(databaseFeedManager));
                orchestration->setLast(std::make_shared<TEventPackageAlertDetailsBuilder>(databaseFeedManager));
                orchestration->setLast(std::make_shared<TEventSendReport>(reportDispatcher));
                orchestration->setLast(std::make_shared<TResultIndexer>(indexerConnector));
                break;

            case ScannerType::PackageDelete:
                orchestration = std::make_shared<TEventDeleteInventory>(inventoryDatabase);
                orchestration->setLast(std::make_shared<TEventPackageAlertDetailsBuilder>(databaseFeedManager));
                orchestration->setLast(std::make_shared<TEventSendReport>(reportDispatcher));
                orchestration->setLast(std::make_shared<TResultIndexer>(indexerConnector));
                break;

            case ScannerType::HotfixInsert:
                orchestration = std::make_shared<THotfixInsert>(databaseFeedManager);
                orchestration->setLast(std::make_shared<TCVESolvedInventorySync>(inventoryDatabase));
                orchestration->setLast(std::make_shared<TCVESolvedAlertDetailsBuilder>(databaseFeedManager));
                orchestration->setLast(std::make_shared<TEventSendReport>(reportDispatcher));
                orchestration->setLast(std::make_shared<TArrayResultIndexer>(indexerConnector));
                break;
            case ScannerType::HotfixDelete: break;

            case ScannerType::Os:
                orchestration = std::make_shared<TOsScanner>(databaseFeedManager);
                orchestration->setLast(std::make_shared<TScanInventorySync>(inventoryDatabase));
                orchestration->setLast(std::make_shared<TEventDetailsBuilder>(databaseFeedManager));
                orchestration->setLast(std::make_shared<TScanOsAlertDetailsBuilder>(databaseFeedManager));
                orchestration->setLast(std::make_shared<TEventSendReport>(reportDispatcher));
                orchestration->setLast(std::make_shared<TResultIndexer>(indexerConnector));
                break;

            case ScannerType::IntegrityClear:
                orchestration = std::make_shared<TCleanAgentInventory>(
                    inventoryDatabase, std::make_shared<TResultIndexer>(indexerConnector));
                orchestration->setLast(std::make_shared<TAlertClearBuilder>(databaseFeedManager));
                orchestration->setLast(std::make_shared<TCleanSendReport>(reportDispatcher));
                break;

            case ScannerType::CleanupAllAgentData:
                orchestration = std::make_shared<TCleanInventory>(inventoryDatabase,
                                                                  std::make_shared<TResultIndexer>(indexerConnector));
                break;

            case ScannerType::ReScanAllAgents:

                orchestration = std::make_shared<TCleanInventory>(inventoryDatabase,
                                                                  std::make_shared<TResultIndexer>(indexerConnector));
                orchestration->setLast(std::make_shared<TBuildAllAgentListContext>());
                orchestration->setLast(std::make_shared<TScanAgentList>(
                    TFactoryOrchestrator::create(ScannerType::PackageInsert,
                                                 databaseFeedManager,
                                                 indexerConnector,
                                                 inventoryDatabase,
                                                 reportDispatcher),
                    TFactoryOrchestrator::create(
                        ScannerType::Os, databaseFeedManager, indexerConnector, inventoryDatabase, reportDispatcher)));
                break;

            case ScannerType::ReScanSingleAgent:
                orchestration = std::make_shared<TCleanAgentInventory>(
                    inventoryDatabase, std::make_shared<TResultIndexer>(indexerConnector));
                orchestration->setLast(std::make_shared<TBuildSingleAgentListInfoContext>());
                orchestration->setLast(std::make_shared<TScanAgentList>(
                    TFactoryOrchestrator::create(ScannerType::PackageInsert,
                                                 databaseFeedManager,
                                                 indexerConnector,
                                                 inventoryDatabase,
                                                 reportDispatcher),
                    TFactoryOrchestrator::create(
                        ScannerType::Os, databaseFeedManager, indexerConnector, inventoryDatabase, reportDispatcher)));
                break;

            case ScannerType::CleanupSingleAgentData:
                orchestration = std::make_shared<TCleanAgentInventory>(
                    inventoryDatabase, std::make_shared<TResultIndexer>(indexerConnector));
                break;

            case ScannerType::GlobalSyncInventory:
                orchestration = std::make_shared<TGlobalSyncInventory>(indexerConnector);
                break;

            default: throw std::runtime_error("Invalid scanner type");
        }

        return orchestration;
    }
};

using FactoryOrchestrator = TFactoryOrchestrator<>;

#endif // _FACTORY_ORCHESTRATOR_HPP
